'''
Created on 7/01/2013

@author: Jorge
'''

from sklearn.grid_search import GridSearchCV
from sklearn.metrics import f1_score, classification_report
from dataset.DatasetImplementations import CustomDataSet
from SVMTreeExample import SVMTreeExample

class Classifier(object):
    '''
    classdocs
    '''
    MINOTIRY_TAG = "minority";
    MAJORITY_TAG = "majority";

    def __init__(self):
        '''
        Constructor
        '''
        self.__feature_selector = None
        self.transformation = []
        self.featureSelector = None
    
    def train (self, X, Y):
        raise NotImplementedError( "train method not implemented" )
    
    def test(self, X, Y):
        raise NotImplementedError( "test method not implemented" )
    
    def _get_X_Y_upper_Y_vectors(self, dataset ):
        X=[]
        Y=[]
        upper_Y=[]
        for e in dataset:
            X.append(e.get_vector_X())
            Y.append( e.get_numerical_category() )
            #upper_Y.append(e.get_numerical_upper_y())
        return X, Y, upper_Y
    
    
    def get_optimal_model(self, classifier, tuned_parameters,X, Y):
        
        clf = GridSearchCV(classifier, tuned_parameters, score_func=f1_score)
        clf.fit(X, Y, cv=5)
        print clf.best_estimator_
        print classification_report(Y, clf.predict(X))
        return clf.best_estimator_
    
    def run_train_and_test(self):
        data = CustomDataSet()
        training_set = data.get_training_set()
        #training_set.extend(data.get_validation_set())
        test_set = data.get_test_set()
        l_training = len(training_set)
        l_test = len(test_set)
        
        #training_set, test_set =  Classifier.convert_example(training_set, test_set)
        X_train, Y_train, upper_Y = self._get_X_Y_upper_Y_vectors(training_set)
        X_test, Y_test, upper_y = self._get_X_Y_upper_Y_vectors(test_set)
        
        if self.featureSelector!=None:
            self.featureSelector.calculate_best_features(X_train,Y_train) #no las calcula. usa las feature obtenidas con validation set
            X_train = self.featureSelector.apply_best_features(X_train)
            X_test = self.featureSelector.apply_best_features(X_test)
        
        for t in self.transformation:
            X = t.apply_transformation(X_train+X_test)
            X_test = X[l_training:l_training+l_test]
            X_train = t.apply_transformation(X_train)
        
        self.train(X_train,Y_train)
        self.test(X_test, Y_test)
        
    def run_train_and_validation(self):
        data = CustomDataSet()
        training_set = data.get_training_set()
        validation_set = data.get_validation_set()
        l_training = len(training_set)
        l_validation = len(validation_set)
        
        #training_set, validation_set =  Classifier.convert_example(training_set, validation_set)
        X_train, Y_train, upper_Y = self._get_X_Y_upper_Y_vectors(training_set)
        X_validation, Y_validation, upper_Y = self._get_X_Y_upper_Y_vectors(validation_set)
        
        if self.featureSelector!=None:
            self.featureSelector.calculate_best_features(X_train,Y_train)
            X_train = self.featureSelector.apply_best_features(X_train)
            X_validation = self.featureSelector.apply_best_features(X_validation)
        
        for t in self.transformation:
            X = t.apply_transformation(X_train+X_validation)
            X_validation = X[l_training:l_training+l_validation]
            X_train = t.apply_transformation(X_train)
        
        self.train( X_train, Y_train)
        self.test( X_validation, Y_validation)
        
        
    def add_transformation_to_data(self, transformation):
        self.transformation.append(transformation)
        
    def setFeatureSelector(self, featureSelector):
        self.featureSelector = featureSelector
        
    def optimal_num_feature(self, name):
            data = CustomDataSet()
            training_set = data.get_training_set()
            validation_set = data.get_validation_set()
            l_training = len(training_set)
            l_validation = len(validation_set)
            
            X_train, Y_train, upper_Y = self._get_X_Y_upper_Y_vectors(training_set)
            X_validation, Y_validation, upper_Y = self._get_X_Y_upper_Y_vectors(validation_set)
            num_features = range(100,4600,100)
            output = open('../data/'+name+'.dat','w')
            output.write(name+"\n")
            output.write('features\tf1\n')
            max_f1=0
            best_indexes=None
            best_num_features = None
            if self.featureSelector!=None:
                for num in num_features:
                    output.write(str(num))
                    for iter in range(5):
                        print "num_feature: ",num
                        self.featureSelector.set_num_features(num)
                        self.featureSelector.calculate_best_features(X_train,Y_train)
                        new_X_train = self.featureSelector.apply_best_features(X_train)
                        new_X_validation = self.featureSelector.apply_best_features(X_validation)
                        for t in self.transformation:
                            new_X_validation = t.apply_transformation(new_X_validation+new_X_train)[0:l_validation]
                            new_X_train = t.apply_transformation(new_X_train)
                            
                        self.train(new_X_train , Y_train)
                        f1 = self.test(new_X_validation, Y_validation)
                        if f1>max_f1:
                            max_f1 = f1
                            best_indexes = self.featureSelector.indexes
                            best_num_features = num
                        output.write('\t'+str(f1))
                    output.write('\n')
                output.write("mejor -> f1="+str(max_f1)+"\tnum_features="+str(best_num_features)+"\tindexes="+str(best_indexes)+"\n")
            output.close()
    

    